import shutil

from siliconcompiler.tools.vpr import VPRTask


class RouteTask(VPRTask):
    '''
    Perform automated route with VPR
    '''
    def __init__(self):
        super().__init__()

        self.add_parameter("max_router_iterations", "int",
                           "set maximum number of routing iterations", defvalue=50)
        self.add_parameter("gen_post_implementation_netlist", "bool",
                           "set to true to have VPR generate a post-implementation netlist",
                           defvalue=False)
        self.add_parameter("timing_corner", "str",
                           "set the timing corner for files generated by the post-implementation "
                           "netlist", defvalue="typical")

    def task(self):
        return "route"

    def setup(self):
        super().setup()

        self.add_input_file(ext="blif")
        self.add_input_file(ext="net")
        self.add_input_file(ext="place")

        self.add_output_file(ext="route")
        self.add_output_file(ext="blif")
        self.add_output_file(ext="net")
        self.add_output_file(ext="place")

        # Timing
        self.add_output_file(ext="vg")
        self.add_output_file(ext="sdc")
        self.add_output_file(ext=f"{self.get('var', 'timing_corner')}.sdf")

        self.add_required_key("var", "max_router_iterations")
        self.add_required_key("var", "gen_post_implementation_netlist")
        self.add_required_key("var", "timing_corner")

        self.set("var", "gen_post_implementation_netlist",
                 self.get("var", "enable_timing_analysis"))

    def runtime_options(self):
        options = super().runtime_options()

        options.append(f"inputs/{self.design_topmodule}.blif")

        options.append("--route")

        # To run only the routing step we need to pass in the placement files
        options.extend(['--net_file', f'inputs/{self.design_topmodule}.net'])
        options.extend(['--place_file', f'inputs/{self.design_topmodule}.place'])

        options.extend(['--max_router_iterations', self.get("var", "max_router_iterations")])

        if self.get("var", "enable_images"):
            graphics_commands = self._get_common_graphics()

            # set_draw_block_text 0 hides the label for various blocks in the design
            # set_draw_block_outlines 0 removes the outline/boundary for various blocks in the
            # design
            # set_routing_util 1 displays the routing utilization as a heat map
            # set_routing_util 4 displays the routing utilization as a heat map over placed blocks
            # Refer: https://github.com/verilog-to-routing/vtr-verilog-to-routing/blob/master/
            # vpr/src/draw/draw_types.h#L89
            # save_graphics saves the block diagram as a png/svg/pdf
            # Refer:
            # https://docs.verilogtorouting.org/en/latest/vpr/command_line_usage/#graphics-options
            graphics_commands.append(
                "set_draw_block_text 0; " +
                "set_draw_block_outlines 0; " +
                "set_routing_util 1; " +
                "save_graphics "
                f"reports/{self.design_topmodule}_route_utilization_with_placement.png;")
            graphics_commands.append(
                "set_draw_block_text 0; " +
                "set_draw_block_outlines 0; " +
                "set_routing_util 4; " +
                "save_graphics "
                f"reports/{self.design_topmodule}_route_utilization.png;")

            options.extend(["--graphics_commands", " ".join(graphics_commands)])

        # Generate a post-implementation netlist for use in external timing analysis
        # if requested.
        if self.get("var", "gen_post_implementation_netlist"):
            # Generate the netlist.
            options.extend(["--gen_post_synthesis_netlist", "on"])
            # Generate the SDC file.
            options.extend(["--gen_post_implementation_sdc", "on"])
            # Create undriven nets for unconnected inputs.
            options.extend(["--post_synth_netlist_unconn_inputs", "nets"])
            # Turn off module parameters.
            options.extend(["--post_synth_netlist_module_parameters", "off"])

        return options

    def post_process(self):
        super().post_process()

        shutil.copy2(f'inputs/{self.design_topmodule}.blif', 'outputs')
        shutil.copy2(f'inputs/{self.design_topmodule}.net', 'outputs')
        shutil.copy2(f'inputs/{self.design_topmodule}.place', 'outputs')

        if self.get("var", "gen_post_implementation_netlist"):
            # These files will only exist if timing analysis is on and VPR was told
            # to generate them.
            shutil.move(f'{self.design_topmodule}_post_synthesis.v',
                        f'outputs/{self.design_topmodule}.vg')
            shutil.move(f'{self.design_topmodule}_post_synthesis.sdc',
                        f'outputs/{self.design_topmodule}.sdc')
            shutil.move(f'{self.design_topmodule}_post_synthesis.sdf',
                        f'outputs/{self.design_topmodule}.{self.get("var", "timing_corner")}.sdf')
        else:
            # If timing analysis is on or we are not generating a post-implementation
            # netlist, just produce blank files. These will trigger any future nodes
            # that use them to skip.
            open(f'outputs/{self.design_topmodule}.vg', 'w')
            open(f'outputs/{self.design_topmodule}.sdc', 'w')
            open(f'outputs/{self.design_topmodule}.{self.get("var", "timing_corner")}.sdf', 'w')
